package com.zmcn.fileTransfer.fw.util;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.URL;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 从网络上获取数据
 * 
 * @author 欧建东
 * @email oyjd@163.com
 * 
 */
public class HttpNet {
	private static String ENCODING = "UTF-8";// 字符编码
	private static boolean CANCONN = true;// 是否可以连接
	private static boolean PROXY_FLAG = false;// 是否用代理
	private static String PROXY_URL;// 代理地址
	private static int PROXY_PORT;// 代理端口
	private static Map<String, String> HEAD_MAP = new HashMap<String, String>();// 客户设置的头

	private static String session = null;// session
	private static String boundary = "----" + System.currentTimeMillis();// 数据分隔符

	/**
	 * 获取session
	 * 
	 * @param session
	 */
	public static void setSession(String session) {
		HttpNet.session = session;
	}

	/**
	 * 设置是否能够连接
	 * 
	 * @param flag
	 */
	public static void setCanConn(boolean flag) {
		CANCONN = flag;
	}

	/**
	 * 设置协议头
	 * 
	 * @param key
	 * @param value
	 */
	public static void setRequestProperty(String key, String value) {
		HEAD_MAP.put(key, value);
	}

	/**
	 * 设置编码
	 * 
	 * @param encoding
	 */
	public static void setEncoding(String encoding) {
		ENCODING = encoding;
	}

	/**
	 * 设置代理
	 * 
	 * @param proxyUrl
	 *            代理地址
	 * @param proxyPort
	 *            代理端口
	 */
	public static void setProxy(String proxyUrl, int proxyPort) {
		PROXY_URL = proxyUrl;
		PROXY_PORT = proxyPort;
		PROXY_FLAG = true;
	}

	/**
	 * 取消代理
	 * 
	 * @param proxyUrl
	 *            代理地址
	 * @param proxyPort
	 *            代理端口
	 */
	public static void cancelProxy() {
		PROXY_FLAG = false;
	}

	/**
	 * 获取特定网络地址的字节数组
	 * 
	 * @param url
	 *            网络地址
	 * @return
	 * @throws Exception
	 */
	public byte[] getBytesFromNet(String url) throws Exception {
		return getBytesFromNet(url, null);
	}

	/**
	 * 获取特定网络地址的字节数组
	 * 
	 * @param url
	 *            网络地址
	 * @param para
	 *            需要post的参数map
	 * @return
	 * @throws Exception
	 */
	public byte[] getBytesFromNet(String url, Map<String, Object> para) throws Exception {
		if (!CANCONN) {
			return null;
		}

		byte[] result = null;
		// ============== 处理资源请求
		// 请求类型，true--post ，false---get
		boolean postFlag = null != para && para.size() > 0;

		// 设置请求协议头
		URL postUrl = new URL(url);
		HttpURLConnection connection;
		if (PROXY_FLAG) {
			Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(PROXY_URL, PROXY_PORT));
			connection = (HttpURLConnection) postUrl.openConnection(proxy);
		} else {
			connection = (HttpURLConnection) postUrl.openConnection();
		}
		connection.setConnectTimeout(10000);
		connection.setUseCaches(false);
		connection.setDoOutput(true);
		connection.setDoInput(true);
		connection.setRequestMethod(postFlag ? "POST" : "GET");
		connection.setRequestProperty("Charset", ENCODING);// 设置请求的编码方式
		connection.setRequestProperty("User-Agent", "Mozilla/5.0 (compatible; MSIE 6.0; Windows NT)");
		if (null != session) {
			connection.setRequestProperty("Cookie", session);
		}
		// 加入自定义协议头
		for (String key : HEAD_MAP.keySet()) {
			connection.setRequestProperty(key, HEAD_MAP.get(key));
		}

		if (postFlag) {
			// 分解普通参数和文件参数
			boolean mutiFlag = false;
			Map<String, String> map_normal = new HashMap<String, String>();
			Map<String, UploadFileVO> map_file = new HashMap<String, UploadFileVO>();
			for (String key : para.keySet()) {
				Object value = para.get(key);
				if (value instanceof UploadFileVO) {
					map_file.put(key, (UploadFileVO) value);
					mutiFlag = true;
				} else {
					map_normal.put(key, value.toString());
				}
			}
			if (mutiFlag) {
				connection.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);
				connection.connect();
				// 合成请求协议体输出
				this.postByMultipart(connection.getOutputStream(), map_normal, map_file);
			} else {
				connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
				connection.connect();
				// 合成请求协议体输出
				this.postByFormUrlencoded(connection.getOutputStream(), map_normal);
			}
		} else {
			connection.setRequestProperty("Content-Type", "text/html");
			connection.connect();
		}

		// ============== 处理回应结果
		if (200 != connection.getResponseCode()) {
			return result;
		}
		// 处理回应的协议头：cookie
		if (null == session) {
			String tmpsession = "";
			Map<String, List<String>> m = connection.getHeaderFields();
			List<String> list = m.get("Set-Cookie");
			if (null != list) {
				for (String s : list) {
					String k = s.substring(0, s.indexOf(";"));
					tmpsession += k + ";";
				}
			}
			session = tmpsession;
		}

		// 处理回应的协议体
		InputStream inputStream = connection.getInputStream();
		ByteArrayOutputStream outStream = new ByteArrayOutputStream();
		byte[] buffer = new byte[1024];
		int len = -1;
		while ((len = inputStream.read(buffer, 0, 1024)) != -1) {
			outStream.write(buffer, 0, len);
		}
		result = outStream.toByteArray();
		outStream.close();
		inputStream.close();
		connection.disconnect();
		return result;
	}

	/**
	 * 下载资源
	 * 
	 * @param url
	 *            网络地址
	 * @param para
	 *            需要post的参数map
	 * @param path
	 *            资源路径
	 * @return
	 * @throws Exception
	 */
	public void downFile(String url, Map<String, Object> para, String path) throws Exception {
		if (!CANCONN) {
			return;
		}

		// ============== 处理资源请求
		// 请求类型，true--post ，false---get
		boolean postFlag = null != para && para.size() > 0;

		// 设置请求协议头
		URL postUrl = new URL(url);
		HttpURLConnection connection;
		if (PROXY_FLAG) {
			Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(PROXY_URL, PROXY_PORT));
			connection = (HttpURLConnection) postUrl.openConnection(proxy);
		} else {
			connection = (HttpURLConnection) postUrl.openConnection();
		}
		connection.setConnectTimeout(30000);
		connection.setUseCaches(false);
		connection.setDoOutput(true);
		connection.setDoInput(true);
		connection.setRequestMethod(postFlag ? "POST" : "GET");
		connection.setRequestProperty("Charset", ENCODING);// 设置请求的编码方式
		connection.setRequestProperty("User-Agent", "Mozilla/5.0 (compatible; MSIE 6.0; Windows NT)");
		if (null != session) {
			connection.setRequestProperty("Cookie", session);
		}
		// 加入自定义协议头
		for (String key : HEAD_MAP.keySet()) {
			connection.setRequestProperty(key, HEAD_MAP.get(key));
		}

		if (postFlag) {
			// 分解普通参数和文件参数
			boolean mutiFlag = false;
			Map<String, String> map_normal = new HashMap<String, String>();
			Map<String, UploadFileVO> map_file = new HashMap<String, UploadFileVO>();
			for (String key : para.keySet()) {
				Object value = para.get(key);
				if (value instanceof UploadFileVO) {
					map_file.put(key, (UploadFileVO) value);
					mutiFlag = true;
				} else {
					map_normal.put(key, value.toString());
				}
			}
			if (mutiFlag) {
				connection.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);
				connection.connect();
				// 合成请求协议体输出
				this.postByMultipart(connection.getOutputStream(), map_normal, map_file);
			} else {
				connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
				connection.connect();
				// 合成请求协议体输出
				this.postByFormUrlencoded(connection.getOutputStream(), map_normal);
			}
		} else {
			connection.setRequestProperty("Content-Type", "text/html");
			connection.connect();
		}

		// ============== 处理回应结果
		if (200 != connection.getResponseCode()) {
			return;
		}
		// 处理回应的协议头：cookie
		if (null == session) {
			String tmpsession = "";
			Map<String, List<String>> m = connection.getHeaderFields();
			List<String> list = m.get("Set-Cookie");
			if (null != list) {
				for (String s : list) {
					String k = s.substring(0, s.indexOf(";"));
					tmpsession += k + ";";
				}
			}
			session = tmpsession;
		}

		// 处理回应的协议体
		InputStream inputStream = connection.getInputStream();
		File aimFile = new File(path);
		aimFile.getParentFile().mkdirs();
		FileOutputStream fos = new FileOutputStream(aimFile);
		byte[] buffer = new byte[10240];
		int len = -1;
		while ((len = inputStream.read(buffer, 0, 1024)) != -1) {
			fos.write(buffer, 0, len);
		}
		fos.flush();
		fos.close();
		inputStream.close();
		connection.disconnect();
	}

	/**
	 * 以x-www-form-urlencoded方式提交，合成协议体
	 * 
	 * @param out
	 *            输出流
	 * @param map_normal
	 *            普通表单字段map
	 * @throws IOException
	 */
	private void postByFormUrlencoded(OutputStream out, Map<String, String> map_normal) throws IOException {
		// 合成POST参数
		StringBuffer sb = new StringBuffer();
		for (String key : map_normal.keySet()) {
			sb.append("&");
			sb.append(key);
			sb.append("=");
			sb.append(URLEncoder.encode(map_normal.get(key).toString(), ENCODING));
		}
		String postData = sb.substring(1);
		byte[] b = postData.getBytes();
		out.write(b, 0, b.length);
		out.flush();
		out.close();
	}

	/**
	 * 以Multipart方式提交，附带文件提交，合成协议体
	 * 
	 * @param out
	 *            输出流
	 * @param map_normal
	 *            普通表单字段map
	 * @param map_file
	 *            文件表单字段map
	 * @throws UnsupportedEncodingException
	 * @throws IOException
	 */
	private void postByMultipart(OutputStream out, Map<String, String> map_normal, Map<String, UploadFileVO> map_file) throws UnsupportedEncodingException, IOException {

		// 普通的表单字段内容
		StringBuilder sb = new StringBuilder();
		for (String key : map_normal.keySet()) {
			sb.append("--");
			sb.append(boundary);
			sb.append("\r\nContent-Disposition: form-data; name=\"");
			sb.append(key);
			sb.append("\"\r\n\r\n");
			sb.append(map_normal.get(key));
			sb.append("\r\n");
		}
		out.write(sb.toString().getBytes(ENCODING));
		out.flush();

		// 上传文件数据
		byte[] buffer = new byte[1024];
		for (String key : map_file.keySet()) {
			UploadFileVO vo = map_file.get(key);
			StringBuilder split = new StringBuilder();
			split.append("--");
			split.append(boundary);
			split.append("\r\nContent-Disposition: form-data; name=\"");
			split.append(key);
			split.append("\"; filename=\"");
			split.append(vo.getFileName());
			split.append("\"\r\nContent-Type: ");
			split.append(vo.getContentType());
			split.append("\r\n\r\n");
			out.write(split.toString().getBytes(ENCODING));
			// 写入文件字节体
			InputStream inputStream = vo.getInputStream();
			if (null != inputStream) {
				int len = 0;
				while ((len = inputStream.read(buffer)) != -1) {
					out.write(buffer, 0, len);
				}
				inputStream.close();
			}

			out.write("\r\n".getBytes(ENCODING));
			out.flush();
		}

		// 写结尾
		out.write(("--" + boundary + "--\r\n").getBytes(ENCODING));

		out.flush();
		out.close();
	}

	/**
	 * 上传网络文件的对象
	 * 
	 * @author 欧建东
	 * @email oyjd@163.com
	 * 
	 */
	public static class UploadFileVO {
		private String fileName;// 文件名
		private String contentType = "application/octet-stream";// 文件类型
		private InputStream inputStream;// 文件流

		public UploadFileVO() {
			super();
		}

		public UploadFileVO(String filePath) {
			this(new File(filePath));
		}

		public UploadFileVO(File file) {
			if (null != file && file.exists()) {
				try {
					this.inputStream = new FileInputStream(file);
					this.fileName = file.getName();
				} catch (FileNotFoundException e) {
					e.printStackTrace();
				}
			}

		}

		public UploadFileVO(String fileName, InputStream inputStream) {
			this.fileName = fileName;
			this.inputStream = inputStream;
		}

		public UploadFileVO(String fileName, String contentType, InputStream inputStream) {
			this.fileName = fileName;
			this.contentType = contentType;
			this.inputStream = inputStream;
		}

		public String getFileName() {
			return fileName;
		}

		public void setFileName(String fileName) {
			this.fileName = fileName;
		}

		public String getContentType() {
			return contentType;
		}

		public void setContentType(String contentType) {
			this.contentType = contentType;
		}

		public InputStream getInputStream() {
			return inputStream;
		}

		public void setInputStream(InputStream inputStream) {
			this.inputStream = inputStream;
		}

	}
}
